#include <prism/frontend/semantic_analysis/resolve/type.hpp>
//=====================================================================
#include <prism/frontend/syntactical_analysis/marshall.hpp>
#include <prism/frontend/semantic_analysis/resolve/function.hpp>
#include <prism/frontend/semantic_analysis/resolve/identifier.hpp>


//=====================================================================
// type_of_pred
//=====================================================================
prism::resolve::type_of_pred::type_of_pred( sooty::const_parseme_ptr_ref N )
	: N(N)
{
}

sooty::parseme_ptr prism::resolve::type_of_pred::operator()() const
{
	ATMA_ASSERT(N);
	
	switch (N->id)
	{
		case ID::int_type:
		case ID::real_type:
		case ID::bool_type:
		case ID::pointer_type:
		case ID::dereference:
			return N;
			
		

		case ID::int_literal:
		case ID::real_literal:
		case ID::bool_literal:
			return N->children.front();

		
		case ID::eq_operator:
			return type_of_pred( N->children[0] )();
		
		case ID::function:
			return type_of_pred( marshall::function::return_type(N) )();
			
		case ID::function_call:
			return type_of_pred( sooty::upwards_bredth_first_find_first_if(N, resolve::function_matches_function_call_pred(N)) )();
			
		case ID::identifier:
			return type_of_pred( prism::marshall::identifier::variable_definition(N) )();
			
		case ID::variable_definition:
			return type_of_pred( marshall::variable_definition::type(N) )();

		case ID::address_of:
		case ID::new_:
		{
			// we need to synthesize a new type
			sooty::parseme_ptr childtype = type_of_pred(N->children[0])();
			sooty::parseme_ptr ptype = sooty::parseme::create( sooty::parseme_ptr(), ID::pointer_type, sooty::value_t(), sooty::lexical_position() );
			ptype->children.push_back(childtype);
			return ptype;
		}
		
	
		case ID::add:
		case ID::sub:
		case ID::mul:
		case ID::div:
		case ID::equ:
		{
			ATMA_ASSERT( types_match_pred()(N->children[0], N->children[1]) );
			return type_of_pred(N->children[0])();
		}

		default: 
		{
			ATMA_ASSERT(false && "not defined");
			return sooty::parseme_ptr();
		}
	}
}



//=====================================================================
// type_matches_pred
//=====================================================================
prism::resolve::type_matches_pred::type_matches_pred( sooty::const_parseme_ptr_ref lhs )
	: lhs(lhs)
{
}

bool prism::resolve::type_matches_pred::operator()( sooty::const_parseme_ptr_ref rhs ) const
{
	sooty::parseme_ptr base_lhs = type_of_pred(lhs)();
	sooty::parseme_ptr base_rhs = type_of_pred(rhs)();
	
	switch (base_lhs->id)
	{
		// for integral types, a simple id comparison is sufficient
		case ID::int_type:
		case ID::real_type:
		case ID::bool_type:
			return base_lhs->id == base_rhs->id;
		
		case ID::pointer_type:
			return rhs->id == ID::pointer_type && types_match_pred()( type_of_pred(base_lhs->children[0])(), type_of_pred(base_rhs->children[0])() );
		
		default:
			ATMA_HALT("bad~~~!");
	}
	
	return false;
}


//=====================================================================
// types_match_pred
//=====================================================================
bool prism::resolve::types_match_pred::operator()( sooty::const_parseme_ptr_ref lhs, sooty::const_parseme_ptr_ref rhs ) const
{
	// zomg lisp
	return type_matches_pred(type_of_pred(lhs)())(type_of_pred(rhs)());
}

